home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1997 September / Macworld (1997-09).dmg / Shareware World / Utilities / Text Processing / Alpha / Tcl / SystemCode / fill.tcl < prev    next >
Text File  |  1997-03-25  |  12KB  |  400 lines

  1. ####################################################################
  2. # Much by Vince Darley.
  3. #                                    created: 3/7/95 {7:49:47 pm} 
  4. #                                last update: 16/5/96 
  5. #  Author: Vince Darley
  6. #  E-mail: <mailto:vince@das.harvard.edu>
  7. #    mail: Division of Applied Sciences, Harvard University
  8. #          Oxford Street, Cambridge MA 02138, USA
  9. #     www: <http://www.fas.harvard.edu/~darley/>
  10. #  
  11. ####################################################################
  12.  
  13. ## 
  14.  # Here's a    brief explanation of the smart fillParagraph routines
  15.  # 
  16.  # 'fillParagraph'
  17.  #       If there's a    selection, then    fill all paragraphs    in that
  18.  #       selection. If not then fill the paragraph surrounding the
  19.  #       insertion point.    The    definition of a    'paragraph'    may    be
  20.  #       mode    dependent (see paraStart, paraFinish)
  21.  #       
  22.  # 'fillOneParagraph'
  23.  #       Fills the single    paragraph surrounding the insertion    point.
  24.  #       If called with parameter    '0', it    doesn't    bother to remember
  25.  #       where the insertion point was, which    makes multiple paragraph
  26.  #       fills quicker when called by    'fillParagraph'
  27.  #       
  28.  # 'rememberWhereYouAre'
  29.  #       Given the start of a    paragraph and the point    to remember,
  30.  #       this    creates    a record stored    in '__g_remember_pos' so that
  31.  #       the following function can find that    spot later,    even after
  32.  #       the paragraph has had space/tabs/new-lines meddled with.
  33.  #       
  34.  # 'goBackToWhereYouWere'
  35.  #       Given the beginning and end of a    selection, where the beginning
  36.  #       corresponds to a    previous call of 'rememberWhereYouAre',    this
  37.  #       procedure will move the insertion point to the correct place.
  38.  #       
  39.  # 'texParaCommands'
  40.  #       A variable containing the bulk of a regexp for paragraph
  41.  #       indicators in 'TeX' mode.
  42.  #       
  43.  # 'paraStart'
  44.  #       Finds the start of the paragraph    containing the insertion point.
  45.  #       
  46.  # 'paraFinish'
  47.  #       Finds the end of    the    paragraph containing the insertion point.
  48.  ##
  49.     
  50. proc fillParagraph {} {
  51.     if {[getPos] == [selEnd]} {
  52.         fillOneParagraph
  53.     } else {    
  54.         set start [getPos]
  55.         set end [selEnd]
  56.         set p $start
  57.         while { $p < $end && $p < [maxPos]} {
  58.             goto $p
  59.             set p [fillOneParagraph 0]
  60.         }
  61.         goto $start
  62.     }
  63. }
  64.  
  65. proc rememberWhereYouAre { startPara pos } {
  66.     global __g_remember_str
  67.     set srem [expr $pos -20 < $startPara ? $startPara : $pos - 20]
  68.     set __g_remember_str [quoteExpr2 [getText $srem $pos ]]
  69.     regsub -all "\[ \t\r\]+" $__g_remember_str {[ \t\r]+} __g_remember_str
  70. }
  71.  
  72. proc goBackToWhereYouWere { start end } {
  73.     global __g_remember_str
  74.     if { $__g_remember_str != "" } {
  75.         regexp -indices ".*(${__g_remember_str}).*" [getText $start $end] wholematch submatch
  76.         set p [expr [info exists submatch] ? \
  77.             [expr $start + 1 + [lindex $submatch 1]] : $end]
  78.         goto [expr $p >= $end ? $end -1 : $p]
  79.     } else {
  80.         goto $start
  81.     }
  82. }
  83.  
  84. ## 
  85.  # -------------------------------------------------------------------------
  86.  #     
  87.  #    "getLeadingIndent" --
  88.  #    
  89.  #     Find the indentation of the line containing 'pos',    and    convert    it
  90.  #     to    a minimal form of tabs followed    by spaces.    If 'size'
  91.  #     is    given, then    the    variable of    that name is set to    the    length of
  92.  #     the indent. Similarly 'halftab' can be set to half a tab.
  93.  # -------------------------------------------------------------------------
  94.  ##
  95. proc getLeadingIndent { pos {size ""} {halftab ""} } {
  96.     # get the leading whitespace of the current line
  97.     set res [search -s -n -f 1 -r 1 "^\[ \t\]*" [lineStart $pos]]
  98.     
  99.     # convert it to minimal form: tabs then spaces, stored in 'front'
  100.     getWinInfo a
  101.     set sp [string range "              " 1 $a(tabsize) ]
  102.     regsub -all $sp [eval getText $res] "\t" front
  103.     regsub -all "\[ \]+\t" $front "\t" front
  104.     if { $size != "" } {
  105.         upvar $size ind
  106.         # get the length of the indent
  107.         regsub -all "\t" $front $sp lfront
  108.         set ind [string length $lfront]
  109.     }
  110.  
  111.     if { $halftab != "" } {
  112.         upvar $halftab ht
  113.         # get the length of half a tab
  114.         set ht [string range "            " 1 [expr $a(tabsize)/2]]
  115.     }
  116.     
  117.     return $front
  118. }
  119.  
  120. proc fillOneParagraph { {remember 1} } {
  121.     global leftFillColumn fillColumn doubleSpaces
  122.  
  123.     set pos [getPos]
  124.     
  125.     set start [paraStart $pos] 
  126.     set end [paraFinish $pos]
  127.     if $remember { rememberWhereYouAre $start $pos }
  128.  
  129.     # Get the leading whitespace of the current line and store length in 'left'
  130.     set front [getLeadingIndent $pos left]
  131.     # fill the text
  132.     regsub -all "\[ \t\r\]+" [string trim [getText $start $end]] " " text
  133.     # turn single spaces at end of sentences into double
  134.     if {$doubleSpaces} {regsub -all {(([^A-Z@]|\\@)[.?!]("|'|'')?([])])?) } $text {\1  } text}
  135. #     if {$doubleSpaces} {regsub -all {(([^A-Z@]|\\@)[.?!][])'"]?) } $text {\1  } text}
  136.  
  137.     # temporarily adjust the fillColumns
  138.     set ol $leftFillColumn
  139.     set or $fillColumn
  140.     set leftFillColumn 0
  141.     set fillColumn [expr $fillColumn - $left]
  142.         
  143.     # break and indent the paragraph
  144.     regsub -all "\r" "\r[string trimright [breakIntoLines $text]]" "\r${front}" text
  145.     
  146.     # don't replace if nothing's changed
  147.     if { "$text\r" != "\r[getText $start $end]" } {
  148.         replaceText $start $end "[string range $text 1 end]\r"
  149.         if $remember { goBackToWhereYouWere $start [expr $start + [string length $text]] }
  150.     }
  151.     
  152.     set leftFillColumn $ol
  153.     set fillColumn  $or
  154.     # in case we wish to fill a region
  155.     return $end
  156. }
  157.  
  158.  
  159. ## 
  160.  # -------------------------------------------------------------------------
  161.  # 
  162.  #    "paraStart"    -- "paraFinish"
  163.  # 
  164.  #     Newly simplified version with fewer regexp    '()' pairs.    Also I think
  165.  #     it    deals better with TeX comments than    the    old    regexp.
  166.  #     
  167.  #     "Start": It's pretty clear    for    non    TeX    modes how this works.  The only    
  168.  #     key is    that we    start at the beginning of the current line and look    back.  
  169.  #     We    then have a    quick check    for    whether    we found that very beginning (in 
  170.  #     which case    return it) or if not (in which case we have found the end of 
  171.  #     the previous paragraph) we move forward a line.
  172.  # 
  173.  #     "Finish": The only    addition is    the    need for an    additional check for
  174.  #     stuff which explicitly    ends lines.
  175.  #       
  176.  #    Results:
  177.  #     The start/finish position of the paragraph containing the given 'pos'
  178.  # 
  179.  # --Version--Author------------------Changes-------------------------------
  180.  #      1.1      <vince@das.harvard.edu> Cut down on '()' pairs
  181.  #    1.2     Vince - March '96          Better filling for TeX tables ('hline')
  182.  #    1.3     Johan Linde - May '96   Now sensitive to HTML elements
  183.  # -------------------------------------------------------------------------
  184.  ##
  185. proc paraStart {pos} {
  186.     global mode texParaCommands htmlParaCommands
  187.     if {$pos == [maxPos]} {incr pos -1}
  188.     set pos [lineStart $pos]
  189.     if { $mode == "TeX" || $mode == "Bib" } {
  190.         set startPara {^[ \t]*$|\\\\[ \t]*$|%.*$|\\h+line[ \t]*$|\$\$[ \t]*$|^[ \t]*(\\(}
  191.         append startPara $texParaCommands {)(\[.*\]|\{.*\}|•)*[ \t]*)+$}
  192.     } elseif {$mode == "HTML"} {
  193.         set startPara {^[ \t]*$|</?(}
  194.         append startPara $htmlParaCommands {)([ \t\r]+[^>]*>|>)}
  195.     } else {
  196.         set startPara {^([ \t]*|([\\%].*))$}
  197.     }
  198.     set res [search -s -n -f 0 -r 1 -l 0 "$startPara" $pos]
  199.     if {![string length $res] || $res == "0 0" } {return 0}
  200.     if { [lindex $res 0] == $pos } {
  201.         return $pos
  202.     } else {
  203.         return [nextLineStart [lindex $res 0]]
  204.     }
  205.     
  206. }
  207.  
  208. set texParaCommands {\[|\]|begin|end|(protect\\)?label|(sub)*section|subfigure|paragraph|centerline|centering|caption|chapter|item|bibitem|intertext}
  209. # The variable htmlParaCommands is defined in html.tcl.
  210. proc paraFinish {pos} {
  211.     global mode texParaCommands htmlParaCommands
  212.     set pos [lineStart $pos]
  213.     set end [maxPos]
  214.     if { $mode == "TeX" || $mode == "Bib" } {
  215.         set endPara {^[ \t]*$|\$\$[ \t]*$|^[ \t]*(\\(}
  216.         append endPara $texParaCommands {)(\[.*\]|\{.*\}|•)*[ \t]*)+$}
  217.     } elseif {$mode == "HTML"} {
  218.         set endPara {^[ \t]*$|</?(}
  219.         append endPara $htmlParaCommands {)([ \t\r]+[^>]*>|>)}
  220.     } else {
  221.         set endPara {^([ \t]*|([\\%].*))$}
  222.     }
  223.     
  224.     set res [search -s -n -f 1 -r 1 -l $end "$endPara" $pos]
  225.     if {![string length $res]} {return $end}
  226.     set cpos [lineStart [lindex $res 0] ]
  227.     if { $cpos == $pos } {
  228.         return [nextLineStart $cpos]
  229.     }
  230.     # A line which ends in '\\', '%...', '\hline', '\hhline'
  231.     # signifies the end of the current paragraph in TeX mode
  232.     # (the above checked for beginning of the next paragraph).
  233.     if { $mode == "TeX" || $mode == "Bib" } {
  234.         set res2 [search -s -n -f 1 -r 1 -l $end {((\\\\|\\h+line)[ \t]*|%.*)$} $pos]
  235.         if [string length $res2] {
  236.             if { [lindex $res2 0] < $cpos } {
  237.                 return [nextLineStart [lindex $res2 0]]
  238.             }
  239.         }
  240.     }
  241.  
  242.     return $cpos
  243.     
  244. }
  245.  
  246.  
  247. proc sentenceParagraph {} {
  248.     set pos [getPos]
  249.     set start [paraStart $pos] 
  250.     set finish [paraFinish $pos]
  251.  
  252.     set t [string trim [getText $start $finish]]
  253.     set period [regexp {\.$} $t]
  254.     regsub -all "\[ \t\r\]+" $t " " text
  255.     regsub -all {\. } $text "Δ" text
  256.     set result ""
  257.     foreach line [split [string trimright $text {.}] "Δ"] {
  258.         if {[string length $line]} {
  259.             append result [breakIntoLines $line] ".\r"
  260.         }
  261.     }
  262.     if {!$period && [regexp {\.\r} $result]} {
  263.         set result [string trimright $result ".\r"]
  264.         append result "\r"
  265.     }
  266.     if {$result != [getText $start $finish]} {
  267.         replaceText $start $finish $result
  268.     }
  269.     goto $pos
  270. }
  271.  
  272. proc getEndpts {} {
  273.     if {[getPos] == [selEnd]} {
  274.         set start [getPos]
  275.         set finish [getMark]
  276.         if {$start > $finish} {
  277.             set temp $start
  278.             set start $finish
  279.             set finish $temp
  280.         }
  281.     } else {
  282.         set start [getPos]
  283.         set finish [selEnd]
  284.     }
  285.     return [list $start $finish]
  286. }
  287.  
  288.  
  289. proc fillRegion {} {
  290.     global leftFillColumn
  291.     set ends [getEndpts]
  292.     set start [lineStart [lindex $ends 0]]
  293.     set finish [lindex $ends 1]
  294.     goto $start
  295.     set text [fillText $start $finish]
  296.     replaceText $start $finish [format "%$leftFillColumn\s" ""] $text "\r"
  297. }
  298.     
  299. proc wrapParagraph {} {
  300.     set pos [getPos]
  301.     set start [paraStart $pos] 
  302.     set finish [paraFinish $pos]
  303.     goto $start
  304.     wrapText $start $finish
  305.     goto $pos
  306. }
  307.  
  308. proc wrapRegion {} {
  309.     set ends [getEndpts]
  310.     set start [lineStart [lindex $ends 0]]
  311.     set finish [lindex $ends 1]
  312.     if {$start == $finish} {
  313.         set finish [maxPos]
  314.     }
  315.     wrapText $start $finish
  316. }
  317.     
  318.  
  319.  
  320. # Remove text from window, transform, and insert back into window.
  321. proc fillText {from to} {
  322.     global doubleSpaces
  323.     set text [getText $from $to]
  324.     regexp {^ *} $text front
  325.     set text [string trim $text]
  326.     regsub -all "\[ \t\r\]+" $text " " text
  327.     if {$doubleSpaces} {regsub -all {(\.|\?|\!) } $text {\1  } text}
  328.     regsub -all "\r" [string trimright [breakIntoLines $text]] "\r${front}" text
  329.     return $front$text
  330. }
  331.  
  332. proc paragraphToLine {} {
  333.     global fillColumn
  334.     global leftFillColumn
  335.     set fc $fillColumn
  336.     set lc $leftFillColumn
  337.     set fillColumn 10000
  338.     set leftFillColumn 0
  339.     fillRegion
  340.     set fillColumn $fc
  341.     set leftFillColumn $lc
  342. }
  343.  
  344. proc lineToParagraph {} {
  345.     global fillColumn
  346.     global leftFillColumn
  347.     set fc $fillColumn
  348.     set fillColumn 75
  349.     set lc $leftFillColumn
  350.     set leftFillColumn 0
  351.     fillRegion
  352.     set fillColumn $fc
  353.     set leftFillColumn $lc
  354. }
  355.  
  356.  
  357. #set sentEnd {[.!?](\r| +)}
  358. set sentEnd {(\r\r|[.!?](\r| +))}
  359. set sentBeg {[\r ][A-Z]}
  360.  
  361. proc nextSentence {} {
  362.     global sentBeg sentEnd
  363.     if {![catch {search -s -f 1 -r 1 $sentEnd [getPos]} mtch]} {
  364.         if {![catch {search -s -f 1 -r 1 -i 0 $sentBeg [expr [lindex $mtch 1]-1]} mtch]} {
  365.             goto [expr [lindex $mtch 0]+1]
  366.         }
  367.     }
  368. }
  369.  
  370.  
  371. proc prevSentence {} {
  372.     global sentBeg sentEnd
  373.     if {[catch {search -s -f 0 -r 1 $sentBeg [expr [getPos]-2]} mtch]} return
  374.     if {![catch {search -s -f 0 -r 1 $sentEnd [lindex $mtch 1]} mtch]} {
  375.         if {![catch {search -s -f 1 -r 1 -i 0 $sentBeg [expr [lindex $mtch 1]-1]} mtch]} {
  376.             goto [expr [lindex $mtch 0]+1]
  377.         }
  378.     }
  379. }
  380. # 5 730 845 955
  381.  
  382. #===============================================================================
  383. # Called by Alpha to do "soft wrapping"
  384. proc softProc {pos start next} {
  385.     global leftFillColumn
  386.     goto $start
  387.     set finish [paraFinish $start]
  388.     set text [fillText $start $finish]
  389.     if {"${text}\r" != [getText $start $finish]} {
  390.         replaceText $start $finish [format "%$leftFillColumn\s" ""] $text "\r"
  391.         return 1
  392.     } else {
  393.         return 0
  394.     }
  395. }
  396.  
  397.  
  398.